﻿#pragma once

#include <functional>

#include <xy/geom/manifold.h>

namespace xy
{

namespace geom
{

struct ellipse_funcs
{
    template <std::size_t N = 0> double fx(double u) const
    {
        return fy<N + 1>(u);
    }

    template <std::size_t N = 0> double fy(double u) const
    {
        if constexpr (N % 4 == 0)
            return std::sin(u);
        else if constexpr (N % 4 == 1)
            return std::cos(u);
        else if constexpr (N % 4 == 2)
            return -std::sin(u);
        else
            return -std::cos(u);
    }
};

struct hyperbola_funcs
{
    template <std::size_t N = 0> double fx(double u) const
    {
        if constexpr (N % 2 == 0)
            return -std::cosh(u);
        else
            return -std::sinh(u);
    }

    template <std::size_t N = 0> double fy(double u) const
    {
        return fx<N + 1>(u);
    }
};

struct parabola_funcs
{
    template <std::size_t N = 0> double fx(double u) const
    {
        if constexpr (N == 0)
            return u * u;
        else if constexpr (N == 1)
            return 2 * u;
        else if constexpr (N == 2)
            return 2;
        else
            return 0;
    }

    template <std::size_t N = 0> double fy(double u) const
    {
        return fx<N + 1>(u);
    }
};

template <std::size_t N, class Funcs> struct conic : public manifold<1, N>
{
    using vecd_t = vec<1, double>;
    using vec_t = vec<N, double>;
    static constexpr Funcs funcs{};

  protected:
    vec_t O, X, Y; // 平面坐标系

  public:
    conic(conic &&) = delete;

    /**
     * @brief 对于椭圆和双曲线，O 为中心，X 为主方向，Y 为副方向
     * 对于抛物线，O 为顶点，X 为对称轴，Y 为顶点切向
     *
     * @param[in] O
     * @param[in] X
     * @param[in] Y
     */
    conic(const vec_t &O, const vec_t &X, const vec_t &Y) : O(O), X(X), Y(Y)
    {
    }

    /**
     * @brief 计算曲线上点的位置
     *
     * @param[in] t
     * @return vec_t
     */
    vec_t d0(const vecd_t &param) const override
    {
        auto t = param[0];
        return O + X * funcs.fx(t) + Y * funcs.fy(t);
    }

    /**
     * @brief 计算曲线上点的位置和导数
     *
     * @param[in] t
     * @return std::array<vec_t, 2>
     */
    std::array<vec_t, 2> d1(const vecd_t &param) const override
    {
        auto t = param[0];
        return {O + X * funcs.fx(t) + Y * funcs.fy(t), X * funcs.fx<1>(t) + Y * funcs.fy<1>(t)};
    }

    /**
     * @brief 计算曲线上点的位置、导数和二阶导数
     *
     * @param[in] t
     * @return std::array<vec_t, 3>
     */
    std::array<vec_t, 3> d2(const vecd_t &param) const override
    {
        auto t = param[0];
        return {O + X * funcs.fx(t) + Y * funcs.fy(t), X * funcs.fx<1>(t) + Y * funcs.fy<1>(t),
                X * funcs.fx<2>(t) + Y * funcs.fy<2>(t)};
    }

    /**
     * @brief 获得 i 维度的参数范围
     *
     * @param[in] i
     * @return std::array<double, 2>
     */
    std::array<double, 2> bound(std::size_t i = 0) const override
    {
        return {-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()};
    }
};

template <std::size_t N> using ellipse = conic<N, ellipse_funcs>;
template <std::size_t N> using hyperbola = conic<N, hyperbola_funcs>;
template <std::size_t N> using parabola = conic<N, parabola_funcs>;

} // namespace geom

} // namespace xy